import { Model, Document } from 'mongoose';
import {
  Get,
  Param,
  Post,
  Put,
  Delete,
  Body,
  Query,
  Req,
} from '@nestjs/common';
import { ApiOperation } from '@nestjs/swagger';
import { defaultPaginate } from './crud-config';
import { get } from 'lodash';
import { CrudOptionsWithModel } from './crud.interface';
import { CrudDto } from './crud.dto';
import { isQueryAll } from '.';

export class CrudPlaceholderDto {
  fake?: string;
  [key: string]: any;
}

export class CrudController {
  constructor(
    public model: Model<{} | any>,
    public crudOptions?: CrudOptionsWithModel,
  ) {}

  @Post('page')
  @ApiOperation({ summary: 'Find all records', operationId: 'list' })
  find(@Body() body: CrudDto) {
    // crud 配置
    const configWhere = get(this.crudOptions, 'routes.find.where', {});
    const configLimit = get(this.crudOptions, 'routes.find.limit', 10);
    const configPopulate = get(
      this.crudOptions,
      'routes.find.populate',
      undefined,
    );
    const configSort = get(this.crudOptions, 'routes.find.sort', undefined);

    // 查询参数
    let [page, limit] = get(body, 'requestBase.page', '1-10')
      ?.split('-')
      .map((item) => parseInt(item)) ?? [1, configLimit];
    const where = get(body, 'data.where', configWhere);
    let skip = get(body, 'requestBase.skip', 0);
    const populate = get(body, 'requestBase.populate', configPopulate);
    const sort = get(body, 'requestBase.sort', configSort);
    const collation = get(body, 'requestBase.collation', undefined);

    if (skip < 1) {
      skip = (page - 1) * limit;
    }

    const find = async () => {
      const total = await this.model.countDocuments(where);

      if (isQueryAll(page, limit)) {
        // 兼容 0-0 查询所有
        skip = 0;
        limit = total;
        page = 1;
      }

      const data = await this.model
        .find()
        .where(where)
        .skip(skip)
        .limit(limit)
        .sort(sort)
        .populate(populate)
        .collation(collation);
      return {
        [defaultPaginate.records]: data,
        [defaultPaginate.current]: page,
        [defaultPaginate.pages]: Math.ceil(total / limit),
        [defaultPaginate.size]: limit,
        [defaultPaginate.total]: total,
      };
    };
    return find();
  }

  @Post(':id')
  @ApiOperation({ summary: 'Find a record' })
  findOne(@Param('id') id: string, @Body() body: CrudDto) {
    const configWhere = get(this.crudOptions, 'routes.findOne.where', {});
    const configPopulate = get(
      this.crudOptions,
      'routes.findOne.populate',
      undefined,
    );
    const configSelect = get(this.crudOptions, 'routes.findOne.select', null);

    const where = get(body, 'data.where', configWhere);
    const populate = get(body, 'requestBase.populate', configPopulate);
    const select = get(body, 'requestBase.select', configSelect);

    return this.model
      .findById(id)
      .populate(populate)
      .select(select)
      .where(where);
  }

  @Post()
  @ApiOperation({ summary: 'Create a record' })
  create(@Body() body: CrudPlaceholderDto) {
    const transform = get(this.crudOptions, 'routes.create.transform');
    if (transform) {
      body = transform(body);
    }
    return this.model.create(body);
  }

  @Put(':id')
  @ApiOperation({ summary: 'Update a record' })
  update(@Param('id') id: string, @Body() body: CrudPlaceholderDto) {
    const transform = get(this.crudOptions, 'routes.update.transform');
    if (transform) {
      body = transform(body);
    }
    return this.model.findOneAndUpdate({ _id: id }, body, {
      new: true,
      upsert: false,
      runValidators: true,
    });
  }

  @Delete(':id')
  @ApiOperation({ summary: 'Delete a record' })
  delete(@Param('id') id: string) {
    return this.model.findOneAndRemove({ _id: id });
  }
}
